package org.netty.framework.httpserver.handle;

import java.lang.reflect.Method;
import java.net.SocketAddress;

import org.netty.framework.core.annotation.HandlerExecutionChain;
import org.netty.framework.core.concurrent.HttpServerThreadPoolExecutor;
import org.netty.framework.core.concurrent.NettyServerThreadPoolExecutor;
import org.netty.framework.core.util.AnnotationContainer;
import org.netty.framework.core.util.JsonUtils;
import org.netty.framework.httpserver.http.HttpServerRequest;
import org.netty.framework.httpserver.http.HttpServerResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;

/**
 * 处理http请求,解析参数,分发请求,返回结果给客户端
 * 
 * @author chenfanglin
 * @date 2018年9月20日下午3:23:48
 */
public class HttpChannelHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

	private static final Logger logger = LoggerFactory.getLogger(HttpChannelHandler.class);

	private static final String FAVICON_ICO = "/favicon.ico";

	private NettyServerThreadPoolExecutor nettyServerThreadPoolExecutor;

	public HttpChannelHandler() {
		nettyServerThreadPoolExecutor = HttpServerThreadPoolExecutor.getInstance().getThreadPoolExecutor();
	}

	/**
	 * channel是活跃状态,可以收发数据
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		SocketAddress socketAddress = ctx.channel().remoteAddress();
		logger.info("远程主机连接成功,远程主机:{}", socketAddress);
	}

	/**
	 * 从channel中读取数据完毕,即调用了channelRead0之后触发.
	 */
	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
	}

	/**
	 * 处理过程中ChannelPipeline中发生错误时被调用
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		logger.error("Channel发生异常:{}", cause);
		ctx.close();
	}

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
		if (FAVICON_ICO.equals(msg.uri())) {
			return;
		}
		HttpServerRequest request = null;
		if (msg.method().equals(HttpMethod.GET)) {
			request = new HttpServerRequest(msg.protocolVersion(), msg.method(), msg.uri());
		}
		if (msg.method().equals(HttpMethod.POST)) {
			request = new HttpServerRequest(msg.protocolVersion(), msg.method(), msg.uri(), msg.content());
		}
		HttpServerResponse response = new HttpServerResponse();
		if (request == null) {
			return;
		}
		request.setRemoteAddress(ctx.channel().remoteAddress());
		request.setLocalAddress(ctx.channel().localAddress());
		nettyServerThreadPoolExecutor.execute(new HttpRequestTask(ctx, request, response));
	}

	/**
	 * 业务线程
	 */
	private class HttpRequestTask implements Runnable {
		private ChannelHandlerContext ctx;
		private HttpServerRequest request;
		private HttpServerResponse response;

		public HttpRequestTask(ChannelHandlerContext ctx, HttpServerRequest request, HttpServerResponse response) {
			this.ctx = ctx;
			this.request = request;
			this.response = response;
		}

		@Override
		public void run() {
			try {
				dispatch(ctx, request, response);
			} catch (Exception e) {
				logger.error("处理http请求失败:{}", e);
			}
		}
	}

	/**
	 * 分发url请求到具体的handle上
	 * 
	 * @param ctx
	 * @param request
	 * @param response
	 */
	private void dispatch(ChannelHandlerContext ctx, HttpServerRequest request, HttpServerResponse response) {
		Object result = null;
		boolean isAutoToJson = true;
		try {
			String path = request.getPath();
			HandlerExecutionChain handle = AnnotationContainer.getHandle(path);
			Method method = handle.getMethod();
			isAutoToJson = handle.isAutoToJson();
			result = method.invoke(handle.getHandle(), request, response);
		} catch (Exception e) {
			response.setStatus(HttpResponseStatus.NOT_FOUND);
			result = "404 Not Found";
		} finally {
			writeResponse(ctx, response, result, isAutoToJson);
		}
	}

	/**
	 * 把结果写入channel
	 * 
	 * @param ctx
	 * @param response
	 * @param result
	 */
	private void writeResponse(ChannelHandlerContext ctx, HttpServerResponse response, Object result,
			boolean isAutoToJson) {
		if (!response.getContentSetted()) {
			try {
				if (isAutoToJson) {
					response.setContent(JsonUtils.objectToJson(result));
				} else {
					response.setContent(String.valueOf(result));
				}
			} catch (Exception e) {
				response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
				logger.error("返回结果转换成json字符串异常:{}", e);
			} finally {
				response.setHeaderIfEmpty(HttpHeaderNames.CONTENT_TYPE.toString(),
						HttpHeaderValues.APPLICATION_JSON.toString());
				response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.getContentLength());
				ctx.writeAndFlush(response);
			}
		}
	}
}
